package com.ggb.reactive.controller.user

import com.ggb.reactive.constant.*
import com.ggb.reactive.domain.dto.OrdersPageQueryDTO
import com.ggb.reactive.domain.dto.OrdersPaymentDTO
import com.ggb.reactive.domain.dto.OrdersSubmitDTO
import com.ggb.reactive.domain.entity.OrderDetail
import com.ggb.reactive.domain.entity.Orders
import com.ggb.reactive.domain.entity.ShoppingCart
import com.ggb.reactive.domain.vo.OrderPaymentVO
import com.ggb.reactive.domain.vo.OrderSubmitVO
import com.ggb.reactive.domain.vo.OrderVO
import com.ggb.reactive.exception.AddressBookBusinessException
import com.ggb.reactive.exception.OrderBusinessException
import com.ggb.reactive.exception.ShoppingCartBusinessException
import com.ggb.reactive.extension.log
import com.ggb.reactive.handler.WebSocketHandler
import com.ggb.reactive.model.PageResult
import com.ggb.reactive.model.WebResult
import com.ggb.reactive.repository.*
import com.ggb.reactive.service.OrderDetailService
import com.ggb.reactive.service.OrderService
import com.ggb.reactive.utils.WeChatPayUtil
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import lombok.RequiredArgsConstructor
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.BeanUtils
import org.springframework.transaction.annotation.Transactional
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import reactor.kotlin.core.publisher.switchIfEmpty
import java.math.BigDecimal
import java.time.Instant
import java.util.UUID

@RestController("userOrderController")
@RequestMapping("/user/order")
@Tag(name = "C端-订单管理")
@RequiredArgsConstructor
class OrderController(
    val orderDetailService: OrderDetailService,
    val orderService: OrderService,
    val webSocketHandler: WebSocketHandler,
    val orderRepository: OrderRepository,
    val orderDetailRepository: OrderDetailRepository,
    val addressBookRepository: AddressBookRepository,
    val shoppingCartRepository: ShoppingCartRepository,
    val weChatPayUtil: WeChatPayUtil,
    val userRepository: UserRepository
) {


    @PostMapping("/submit")
    @Transactional
    @Operation(summary = "提交订单")
    fun submit(
        @RequestBody ordersSubmitDTO: OrdersSubmitDTO,
        exchange: ServerWebExchange
    ): Mono<WebResult<OrderSubmitVO>> {
        val userId = (exchange.attributes[JwtClaimsConstant.USER_ID] as Int).toLong()
        log.info("userId: {}", userId)
        log.info("ordersSubmitDTO: {}", ordersSubmitDTO)
        return addressBookRepository.findById(ordersSubmitDTO.addressBookId!!)
            .switchIfEmpty(Mono.error(AddressBookBusinessException(MessageConstant.ADDRESS_BOOK_IS_NULL)))
            .flatMap { addressBook ->
                shoppingCartRepository.findAllByUserId(userId)
                    .collectList()
                    .switchIfEmpty(Mono.error(ShoppingCartBusinessException(MessageConstant.SHOPPING_CART_IS_NULL)))
                    .flatMap { shoppingCarts ->
                        val orders = Orders().apply {
                            BeanUtils.copyProperties(ordersSubmitDTO, this)
                            orderTime = Instant.now()
                            this.userId = userId
                            status = OrderStatus.PENDING_PAYMENT.getStatus()
                            payStatus = OrderPayStatus.UN_PAID.getStatus()
                            number = UUID.randomUUID().toString()
                            phone = addressBook.phone
                            consignee = addressBook.consignee
                        }
                        orderRepository.save(orders)

                            .flatMap { savedOrder ->
                                val orderDetailList = shoppingCarts.map { cart ->
                                    OrderDetail().apply {
                                        BeanUtils.copyProperties(cart, this)
                                        orderId = savedOrder.id
                                        id = null
                                    }
                                }
                                orderDetailRepository.saveAll(orderDetailList)
                                    .collectList()
                                    .map {
                                        OrderSubmitVO(
                                            id = savedOrder.id,
                                            orderNumber = savedOrder.number,
                                            orderAmount = savedOrder.amount,
                                            orderTime = savedOrder.orderTime
                                        )
                                    }
                            }

                    }
            }
            .doOnSuccess {
                val map = hashMapOf<String, Any>()
                map["orderId"] = it.id!!
                map["type"] = NotificationConstant.NEW_ORDER.getStatus()
                map["content"] = "订单号：创建订单"
                val json = JSONObject(map).toString()
                webSocketHandler.sendToAllClient(json)
            }
            .flatMap { orderSubmitVO ->
                shoppingCartRepository.deleteAllByUserId(userId)
                    .thenReturn(WebResult.success(orderSubmitVO))
            }


    }

    /**
     *  status 订单状态 1待付款 2待接单 3已接单 4派送中 5已完成 6已取消
     */
    @GetMapping("/historyOrders")
    @Operation(summary = "历史订单查询")

    fun page(
        page: Int,
        pageSize: Int,
        status: Int? = null,
        exchange: ServerWebExchange
    ): Mono<WebResult<PageResult<OrderVO>>> {
        val userId = (exchange.attributes[JwtClaimsConstant.USER_ID] as Int).toLong()
        val ordersPageQueryDTO = OrdersPageQueryDTO().apply {
            this.userId = userId
            this.page = page
            this.pageSize = pageSize
            this.status = status
        }
        return orderService.findOrdersByCriteria(ordersPageQueryDTO)
            .flatMap { pageResult ->
                Flux.fromIterable(pageResult.records!!)
                    .flatMap { order ->
                        orderDetailService.findAllByOrderId(order.id!!)
                            .collectList()
                            .map { details ->
                                val orderVO = OrderVO()

                                BeanUtils.copyProperties(order, orderVO)
                                orderVO.orderDetailList = details
                                orderVO.orderDishes = details.joinToString(separator = ";") {
                                    "${it.name}${it.number}"
                                }
                                orderVO
                            }
                    }
                    .collectList()
                    .map { orderVOs ->
                        WebResult.success(PageResult(total = pageResult.total, records = orderVOs))
                    }
            }
    }

    @GetMapping("/orderDetail/{id}")
    @Operation(summary = "查询订单详情")
    fun detail(@PathVariable id: Long): Mono<WebResult<OrderVO>> {
        return orderRepository.findById(id)
            .flatMap { order ->
                orderDetailService.findAllByOrderId(order.id!!)
                    .collectList()
                    .map { details ->
                        val orderVO = OrderVO()
                        BeanUtils.copyProperties(order, orderVO)
                        orderVO.orderDetailList = details
                        orderVO.orderDishes = details.joinToString(separator = ";") {
                            "${it.name}${it.number}"
                        }
                        orderVO
                    }
            }
            .map {
                WebResult.success(it)
            }
    }

    @PutMapping("/cancel/{id}")
    @Operation(summary = "取消订单")
    fun cancel(@PathVariable id: Long): Mono<WebResult<String>> {
        return orderRepository.findById(id)
            .switchIfEmpty {
                Mono.error(OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR))
            }
            .flatMap { order ->
                if (order.status!! > OrderStatus.TO_BE_CONFIRMED.getStatus()) {
                    Mono.error(OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR))
                } else {
                    order.status = OrderStatus.CANCELLED.getStatus()
                    orderRepository.save(order)
                        .thenReturn(WebResult.success(""))
                }
            }
    }


    @PostMapping("/repetition/{id}")
    @Operation(summary = "再来一单")
    fun repetition(@PathVariable id: Long, exchange: ServerWebExchange): Mono<WebResult<String>> {
        val userId = (exchange.attributes[JwtClaimsConstant.USER_ID] as Int).toLong()
        return orderDetailService.findAllByOrderId(id)
            .collectList()
            .flatMap { detailList ->
                val shoppingCartList = detailList.map {
                    val shoppingCard = ShoppingCart()
                    BeanUtils.copyProperties(it, shoppingCard, "id")
                    shoppingCard.userId = userId
                    shoppingCard.createTime = Instant.now()
                    shoppingCard
                }
                shoppingCartRepository.saveAll(shoppingCartList)
                    .then(Mono.just(WebResult.success()))
            }
    }

    @GetMapping("/reminder/{id}")
    @Operation(summary = "催单")
    fun reminder(@PathVariable id: Long): Mono<WebResult<String>> {
        return orderRepository.findById(id)
            .flatMap {
                val map = hashMapOf<String, Any>()
                map["orderId"] = it.id!!
                map["type"] = NotificationConstant.REMIND_ORDER.getStatus()
                map["content"] = "订单号：创建订单"
                val json = JSONObject(map).toString()
                webSocketHandler.sendToAllClient(json)
                Mono.just(WebResult.success())
            }
    }

    @PutMapping("/payment")
    @Operation(summary = "订单支付")
    fun payment(
        @RequestBody ordersPaymentDTO: OrdersPaymentDTO,
        exchange: ServerWebExchange
    ): Mono<WebResult<OrderPaymentVO>> {
        val userId = (exchange.attributes[JwtClaimsConstant.USER_ID] as Int).toLong()
        return userRepository.findById(userId).flatMap { user ->
            weChatPayUtil.pay(
                ordersPaymentDTO.orderNumber!!,
                BigDecimal(0.01), "苍穹外卖订单", user.openid!!
            ).map { jsonObject ->
                if (jsonObject.getString("code") != null && jsonObject.getString("code").equals("ORDERPAID")) {
                    WebResult.error<String>(MessageConstant.ORDER_HAS_PAY)
                }
                val orderPaymentVO = jsonObject.toJavaObject(OrderPaymentVO::class.java)
                WebResult.success(orderPaymentVO)
            }
        }

    }
}